import datetime
import fcntl
import os
import sys
import json
import time
import inspect
from aiohttp import ClientSession
from aiohttp import web
from aiohttp.client import request
import asyncio
import aiohttp
import logging
import traceback
from utils import Utils


class ProcessLock(object):

    __lockfd = None

    @staticmethod
    def lock(process):
        ProcessLock.__lockfd = open(process+'.lock', 'a+')
        try:
            fcntl.flock(ProcessLock.__lockfd, fcntl.LOCK_EX|fcntl.LOCK_NB)
            return 0
        except Exception as e:
            print(e)
            traceback.print_exc()
            return -1
    @staticmethod
    def unlock():
        fcntl.flock(ProcessLock.__lockfd, fcntl.LOCK_UN)



class ServiceBase:
    class Handler:
        def __init__(self, handler):
            argspec = inspect.getargspec(handler)
            self.params = [arg for arg in argspec.args]
            self.params.remove("self")
            self.func = handler
    
    def __init__(self):
        self._handler_mapping = {}
        self._handlers = []

    def startup_webservice(self, port):
        for handler in self._handlers:
            self._handler_mapping[handler.__name__] = ServiceBase.Handler(handler)
        
        loop = asyncio.get_event_loop()
        app = web.Application(loop=loop)

        for handler in self._handler_mapping:
            app.router.add_post("/%s"%(handler), self.process_json_call)
            app.router.add_get("/%s"%(handler), self.process_json_call)
        localip = Utils.get_host_ip()
        svr = yield from loop.create_server(protocol_factory=app.make_handler(), host=localip,
                        port=port)
        logging.error("service started at http://%s:%d"%(localip,port))
        return svr        

    def add_handler(self, handler):
        if handler not in self._handlers:
            self._handlers.append(handler)
        else:
            raise Exception("duplicated handler %s"%handler)

    def compose_response(self, data, err, uid, seq):
        # 响应中error默认为null
        result = dict(data=data, error=None, uid=uid, utime=time.time(), seq=seq)
        error = dict(code=int(err), message=str(err))
        if err != 0:
            result['error'] = error
        return result

    async def process_json_call(self, request):
        result = None
        uid = None
        resp = None
        ret = 0
        start_timestamp = time.time()
        start_datetime = datetime.datetime.fromtimestamp(start_timestamp)
        try:
            path_chunks = request.path.split("/")
            handler_key = ""
            for chunk in path_chunks:
                if not chunk:
                    continue
                handler_key += chunk + "_"
            handler_key = handler_key[:-1]
            handler = self._handler_mapping[handler_key]
            packet = {}
            if request.content_length:
                packet = await request.json()
            # 合并get querystring 和body参数
            for k, v in request.query.items():
                packet.setdefault(k, v)
            params = []
            for param in handler.params:
                if param not in packet:
                    params.append(None)
                else:
                    params.append(packet[param])
            print(params)
            retcode, result = await handler.func(*params)
            resp = self.compose_response(result, retcode, uid, 0)
        except Exception as e:
            logging.error(traceback.format_exc())
            logging.error(e)
            if ret == 0:
                ret = -1
            resp = self.compose_response(None, ret, uid, 0)
        end_timestamp = time.time()
        end_datetime = datetime.datetime.fromtimestamp(end_timestamp)
        spend = end_timestamp - start_timestamp
        logging.info(f"{request} start at {start_datetime}, end at {end_datetime}, spend{spend}, {resp}")
        return web.Response(text = json.dumps(resp), content_type = 'application/json')

    def start(self, cfg):
        pass

    def reload_cfg(self):
        self._config = json.load(open("%s.json"%self._process_name, 'r',encoding="utf8"))
        return self._config

    def main(self, name):
        lock = ProcessLock
        locked = 0
        try:
            self._process_name = name
            locked = lock.lock(self._process_name)
            if locked:
                print('process already running!')
                sys.exit(2)
            if not os.path.exists("./log"):
                os.mkdir("log")
            Utils.init_logging(self._process_name)
            cfg = self.reload_cfg()
            self.start(cfg)
        except Exception as e:
            print(e)
            traceback.print_exc()
            lock.unlock()
            sys.exit(3)
        finally:
            if locked == 0:
                lock.unlock()
        sys.exit(0)
